package crypto

import (
	"io"

	"github.com/dromara/dongle/crypto/keypair"
	"github.com/dromara/dongle/crypto/sm2"
)

// BySm2 encrypts by SM2.
func (e Encrypter) BySm2(kp *keypair.Sm2KeyPair) Encrypter {
	if e.Error != nil {
		return e
	}
	// Streaming encryption mode
	if e.reader != nil {
		e.dst, e.Error = e.stream(func(w io.Writer) io.WriteCloser {
			return sm2.NewStreamEncrypter(w, kp)
		})
		return e
	}
	// Standard encryption mode
	if len(e.src) > 0 {
		e.dst, e.Error = sm2.NewStdEncrypter(kp).Encrypt(e.src)
	}
	return e
}

// BySm2 decrypts by SM2.
func (d Decrypter) BySm2(kp *keypair.Sm2KeyPair) Decrypter {
	if d.Error != nil {
		return d
	}
	// Streaming decryption mode
	if d.reader != nil {
		d.dst, d.Error = d.stream(func(r io.Reader) io.Reader {
			return sm2.NewStreamDecrypter(r, kp)
		})
		return d
	}
	// Standard decryption mode
	if len(d.src) > 0 {
		d.dst, d.Error = sm2.NewStdDecrypter(kp).Decrypt(d.src)
	}
	return d
}

// BySm2 signs by SM2.
func (s Signer) BySm2(kp *keypair.Sm2KeyPair) Signer {
	if s.Error != nil {
		return s
	}

	// Streaming signing mode
	if s.reader != nil {
		s.sign, s.Error = s.stream(func(w io.Writer) io.WriteCloser {
			return sm2.NewStreamSigner(w, kp)
		})
		return s
	}

	// Standard signing mode
	if len(s.data) > 0 {
		s.sign, s.Error = sm2.NewStdSigner(kp).Sign(s.data)
	}

	return s
}

// BySm2 verifies by SM2.
func (v Verifier) BySm2(kp *keypair.Sm2KeyPair) Verifier {
	if v.Error != nil {
		return v
	}

	// Streaming verification mode
	if v.reader != nil {
		verifier := sm2.NewStreamVerifier(v.reader, kp)

		// Write the data to be verified
		if len(v.data) > 0 {
			_, v.Error = verifier.Write(v.data)
		}

		// Close the verifier to perform verification
		v.Error = verifier.Close()
		if v.Error != nil {
			return v
		}

		v.verify = true
		return v
	}

	// Standard verification mode
	if len(v.data) > 0 {
		if len(v.sign) == 0 {
			v.Error = &keypair.EmptySignatureError{}
			return v
		}

		valid, err := sm2.NewStdVerifier(kp).Verify(v.data, v.sign)
		if err != nil {
			v.Error = err
			return v
		}
		if valid {
			v.verify = true
		}
	}

	return v
}
